package java_thinking.unit_21._21_5;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Desc:   5-17 用ConcurrentHashMap 替换 5-16 的 HashMap 但是还是存在风险，不要这么做
 * @author: cww
 * @DateTime: 2019/6/10 18:42
 */

public class Memoizer2<A, V> implements Computable<A, V> {
    private final Map<A, V> cache = new ConcurrentHashMap<>();
    private final Computable<A, V> c;

    public Memoizer2(Computable<A, V> c) {
        this.c = c;
    }

    /**
     * 问题在于，如果某个线程启动了一个开销很大的计算，而其他线程并不知道这个计算正在进行
     * 那么很可能会重复这个计算；虽然 cache 的插入与查询是线程安全的，但是查询出 cache
     * 进行比对这些操作不是原子的；可能算出了result正打算put进 cache 时另一个线程进来 get
     * 还是空的所以又进行一次计算；
     * 避免上面问题的最高效的方法就是等待已知正在计算的线程结束再查询缓存；使用 FutureTask，
     * FutureTask 表示一个计算过程，如果结果可用那么立即返回结果，否则已知阻塞，直到计算出来
     * @param arg
     * @return
     * @throws InterruptedException
     */
    @Override
    public V compute(A arg) throws InterruptedException {
        //这里的 get 其实是在判断某个计算是否已经完成
        V result = cache.get(arg);
        if (result == null) {
            result = c.compute(arg);
            cache.put(arg, result);
        }
        return result;
    }
}
